// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
//===------------ Evaluator.h
//
//             This file is part of the reactor project
//                Author: R-Core Team
//
//===---------------------------------------------------------------------===//

#ifndef _EVALUATOR_H_
#define _EVALUATOR_H_

#include "MutatorThread.h"
#include "Environment.h"
#include "Stack.h"
#include "Promise.h"

#include "bc.h"

#include "vmkit/System.h"
#include "vmkit/Locks.h"

#include <llvm/ExecutionEngine/GenericValue.h>

#define JITTEST 1

namespace R {
	class Reactor;
	class RModule;
	class FunInfo;
	class Environment;
	class Any;
	class Closure;
	class ConstPool;

	class Evaluator : public vmkit::MutatorThread {
		friend class Debugger;
	public:
		EnvInfo*          envInfo;
		RModule*          module;

	private:
		int        pc;    // current instruction
		Stack*     stack;
		int*       cs;
		ConstPool* cpool;
		bool       run;

		enum ExecutionState {EVAL_TO_EVAL, EVAL_TO_JIT, JIT_JIT};
		unsigned char es; // execution state

	public:
		Evaluator(Reactor* vm);
		virtual ~Evaluator(); /* TODO: Must verify that this destructor is called when a thread die */

		Reactor* vm();

		virtual void tracer(word_t closure);

		int getPC() { return pc; }
		int *getCS() { return cs; }

		Stack* getStack() { return stack; }

		AnyStack* getAnyStack();

		Any* getVar(Symbol* symb, bool letInStack=0);


		// Manipulation of contexts
		inline void saveContext();
		inline void restoreContext();
		inline void changeContext(EnvInfo* new_envInfo, RModule *new_mod, uint64_t new_pc);


		inline void callLambda(EnvInfo* new_envInfo, FunInfo *fun);
		inline void returnLambda(bool isCall);

		inline void cleanStack(bool isCall);

		// JIT related functions
		inline void jit_saveContext();
		inline void jit_changeContext(EnvInfo* new_envInfo, RModule* mod);
		inline void jit_restoreContext();
		inline void callJITLambda(EnvInfo* new_envInfo, FunInfo *fun);
		inline void returnEvalToJITFunction(uint64_t nbArgs, Any* retVal);
		inline void returnJITFunction(Any* retVal);
		inline void returnJITPromise(Promise* prom, Any* retVal);
		void jit_CALLBUILTIN (uint64_t p1, Any ** p2, uint64_t nbArgs);
		void jit_CALLSPECIAL (uint64_t p1, uint64_t nbArgs);
		void jit_GETVAR (Any ** p1);
		void jit_CALL (Any ** sym, uint64_t pnames, uint64_t nbArgs);
		void JITStackFinalize();
		void runEvalToJITFunction(Closure* val, uint64_t nbArgs, cpool_entry *pdesc);
		void runEvalToJITPromise(Promise* prom);
		inline void runJITToJITPromise(Promise* prom);
		inline void runJITToJITFunction(Closure* val, uint64_t nbArgs, cpool_entry *pdesc);

		Closure *makeClosure(int closure_addr, cpool_entry *fp_addr, Environment *parent);
		void matchParams(uint32_t stack_args, cpool_int *kw_descr, FunInfo* fun, EnvInfo* env, int32_t* result);
		void fastMatchParams(uint32_t nbArgs, FunInfo *fun, EnvInfo *env, void *matchedArgs);
		void jit_matchParams(uint32_t stack_args, cpool_int *kw_descr, FunInfo *fun, EnvInfo *env);

#define ARGIDX(__x) Any **
#define ARGIMM(__x) uint64_t
#define ARGLBL(__x) uint64_t
#define ARGIOL(__x) void *
#define ARGIFI(__x) FunInfo*

#define BCP0(__x)
#define BCP1(__x)\
	ARG##__x(1) p1
#define BCP2(__x, __y)\
	ARG##__x(2) p1, ARG##__y(1) p2
#define BCP3(__x, __y, __z)\
	ARG##__x(3) p1, ARG##__y(2) p2, ARG##__z(1) p3
#define BCP4(__x, __y, __z, __a)\
	ARG##__x(4) p1, ARG##__y(3) p2, ARG##__z(2) p3, ARG##__a(1) p4

#define CFDECL_CF_NOP() void
#define CFDECL_CF_RET() void 
#define CFDECL_USE_EVALSTACK() void
#define CFDECL_CF_BT(i) uint64_t

#define APPLY_BC(__x, __p, __cf) inline CFDECL_##__cf eval_ ## __x (BC##__p);
#include "bc.def"

#undef ARGIDX
#undef ARGIMM
#undef ARGLBL
#undef ARGIOL
#undef ARGIFI
#undef BCP0
#undef BCP1
#undef BCP2
#undef BCP3
#undef BCP4
#undef CFDECL_CF_NOP
#undef CFDECL_CF_RET
#undef CFDECL_USE_EVALSTACK
#undef CFDECL_CF_BT

	public:
		static void (Evaluator::*map[])();

		static Evaluator* get();
		
		// execute - Once file to exectue has been opened, this
		// method is called in order to execute the bytecode.
		void execute(RModule* module, Environment *top_level);

		// Terminate properly this machine
		void stop();
	};
}

#endif
